﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using Less.Tools;

public static class Extensions
{
    #region Object
    /// <summary>
    /// Converts my value to another type
    /// </summary>
    /// <typeparam name="T">The target type expected</typeparam>
    /// <param name="me">My value</param>
    /// <param name="returnDefaultWhenException">A flag to return defalt values or exceptions. true=defaults :-)</param>
    /// <returns>The value converted</returns>
    public static T ConvertTo<T>(this object me, bool returnDefaultWhenException = true)
    {
        Type targetType = typeof(T);
        Type sourceType = ( me == null ) ? typeof(object) : me.GetType();
        try
        {
            if ( targetType.IsEnum )
            {
                return (T)Enum.Parse(targetType, me.ToString());
            }
            else
            {
                Delegate converter = BuildConversion(sourceType, targetType);
                return (T)converter.DynamicInvoke(me);
            }
        }
        catch ( Exception )
        {
            if ( returnDefaultWhenException )
            {
                return default(T);
            }
            else
            {
                throw;
            }
        }
    }
    /// <summary>
    /// A delegated created for concrete conversion cases
    /// </summary>
    /// <param name="fromType">Original type</param>
    /// <param name="toType">Target Type</param>
    /// <returns>A conversion method</returns>
    private static Delegate BuildConversion(Type fromType, Type toType)
    {
        ParameterExpression input = Expression.Parameter(fromType, "input");
        Expression body;
        try
        {
            body = Expression.Convert(input, toType);
        }
        catch ( InvalidOperationException )
        {
            ConstantExpression conversionType = Expression.Constant(toType);
            body = Expression.Call(typeof(Convert), "ChangeType", null, input, conversionType);
        }
        LambdaExpression result = Expression.Lambda(body, input);
        return result.Compile();
    }
    /// <summary>
    /// Serializes an object to JSON string notation, and optinal writes ti to a file
    /// </summary>
    /// <param name="me">The object that will be serialized</param>
    /// <param name="filePath">The full path name where saves the serailization. Default is null for not saving</param>
    /// <returns>A string with the JSON representation</returns>
    public static string Serialize(this object me, string filePath = null)
    {
        if ( filePath == null )
            return Functions.Serialize(me);
        else
            return Functions.Serialize(me, filePath);
    }

    /// <summary>
    /// Deserializes a string with a JSON representation of an object into an instance of that object
    /// </summary>
    /// <typeparam name="T">The object type</typeparam>
    /// <param name="me">The string with the JSON</param>
    /// <returns>The object instance</returns>
    public static T Deserialize<T>(this string me)
    {
        if ( me.HasValue() )
        {
            return Functions.DeserializeString<T>(me);
        }
        else
        {
            return default(T);
        }
    }

    /// <summary>
    /// Deserializes a string with a JSON representation of an object into an instance of that object
    /// Needs useSerializerExtension =true when serialized
    /// </summary>
    /// <param name="me">The string with the JSON</param>
    /// <returns>The object instance</returns>
    public static object Deserialize(this string me)
    {
        if ( me.HasValue() )
        {
            return Functions.DeserializeString(me);
        }
        else
        {
            return null;
        }
    }
    #endregion
    #region String
    /// <summary>
    /// Determines whether the specified string is null or empty.
    /// </summary>
    /// <param name = "me">The string to check.</param>
    /// /// <returns>
    /// <c>true</c> if this string doesn't have value; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsNullOrEmpty(this string me)
    {
        return me == null || String.IsNullOrEmpty(me.Trim());
    }
    /// <summary>
    /// Determines whether a string is not null nor empty.
    /// </summary>
    /// <param name="me">The string to check</param>
    /// <returns>
    /// <c>true</c> if this string has value; otherwise, <c>false</c>.
    /// </returns>
    public static bool HasValue(this string me)
    {
        return !me.IsNullOrEmpty();
    }
    /// <summary>
    /// Returns the left part of the string, or the full string if it is smaller
    /// </summary>
    /// <param name="me">The original string.</param>
    /// <param name="characterCount">The character count to be returned.</param>
    /// <returns>The left part</returns>
    public static string Left(this string me, int characterCount)
    {
        if ( me.Length >= characterCount )
        {
            return me.Substring(0, characterCount);
        }
        else
        {
            return me;
        }
    }
    /// <summary>
    /// Returns the Right part of the string, or the full string if it is smaller
    /// </summary>
    /// <param name="me">The original string.</param>
    /// <param name="characterCount">The character count to be returned.</param>
    /// <returns>The right part</returns>
    public static string Right(this string me, int characterCount)
    {
        if ( me.Length >= characterCount )
        {
            return me.Substring(me.Length - characterCount);
        }
        else
        {
            return me;
        }
    }
    /// <summary>
    /// Returns a fixed number of chars from the the start, paddig with balnks whe is smaller
    /// <remarks>Usefull for formatting </remarks>
    /// </summary>
    /// <param name="me">The original string.</param>
    /// <param name="characterCount">The character count to be returned.</param>
    /// <returns>The left part, padded with blanks</returns>
    public static string FixedLenght(this string me, int characterCount)
    {
        return me.Left(characterCount).PadRight(characterCount);
    }
    /// <summary>
    /// Tests whether the contents of a string is a numeric value
    /// </summary>
    /// <param name = "me">String to check</param>
    /// <returns>
    /// Boolean indicating whether or not the string contents are numeric
    /// </returns>
    /// <remarks>
    /// Contributed by Kenneth Scott
    /// </remarks>
    public static bool IsNumeric(this string me)
    {
        float output;
        return Single.TryParse(me, out output);
    }
    #endregion
    #region IList
    /// <summary>
    /// Determines whether a collection is null or has no elements without having to enumerate the entire collection to get a count.  Uses LINQ.
    /// </summary>
    /// <typeparam name="T">The item type.</typeparam>
    /// <param name="me">The items.</param>
    /// <returns>
    /// <c>true</c> if this list is null or empty; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsNullOrEmpty<T>(this IList<T> me)
    {
        return me == null || !me.Any();
    }

    /// <summary>
    /// Determines whether a collection is null or has no elements without having to enumerate the entire collection to get a count.  Uses LINQ.
    /// </summary>
    /// <typeparam name="T">The item type.</typeparam>
    /// <param name="me">The items.</param>
    /// <returns>
    /// <c>true</c> if this list has value; otherwise, <c>false</c>.
    /// </returns>
    public static bool HasValue<T>(this IList<T> me)
    {
        return me != null && me.Any();
    }
    #endregion
    #region Exception
    /// <summary>
    /// Overrides the default representation of an Exception
    /// </summary>
    /// <param name="me">The exception to create the string from.</param>
    /// <returns></returns>
    public static string ToLogString(this Exception me)
    {
        if ( me != null )
        {
            int level = 0;
            const string tab = " := "; // in notepad++ with language smalltalk displays in red
            Exception currentException = me;
            string exceptionType = currentException.GetType() + ":";
            var msg = new StringBuilder(exceptionType);
            msg.Append(tab);
            string baseExceptionType = currentException.GetBaseException().GetType() + ":";
            if ( baseExceptionType.HasValue() && exceptionType != baseExceptionType && baseExceptionType != "Exception:" )
            {
                msg.Append(baseExceptionType);
                msg.Append(tab);
            }
            try
            {
                while ( currentException != null )
                {
                    if ( level > 0 )
                    {
                        msg.Append(" lvl:" + level);
                    }
                    msg.Append(currentException.Message);
                    if ( me.StackTrace.HasValue() )
                    {
                        msg.Append(tab);
                        msg.Append("Stk:");
                        msg.Append(ShortStackTrace(me.StackTrace));
                    }
                    if ( me.Source.HasValue() )
                    {
                        msg.Append(tab);
                        msg.Append("Src:");
                        msg.Append(me.Source);
                    }
                    currentException = currentException.InnerException;
                    level++;
                }
            }
            finally
            {
            }
            return msg.ToString();
        }
        else
        {
            return "";
        }
    }


    /// <summary>
    /// Format to get less verbose logs
    /// </summary>
    /// <param name="me"></param>
    /// <returns></returns>
    private static string ShortStackTrace(string me)
    {
        //-    en Test.LessFramework.LogConfig() en C:\Users\Alberto Basalo\Dropbox\Work\Code\LessFramework\Test\LessFramework.cs:línea 53
        int init = me.IndexOf(@"C:\");
        int end = me.IndexOf(@".cs");
        int length = end - init;
        if ( init == -1 || end == -1 || length<0 || init+length>=me.Length)
        {
            return me;
        }
        string pathName = me.Substring(init, length);
        string dirName = Path.GetDirectoryName(pathName);
        me = me.Replace(dirName, "");
        //-    en Test.LessFramework.LogConfig() en \LessFramework.cs:línea 53
        return me;
    }
    #endregion
    #region MethodInfo
    public static string ToFriendlyName(this MethodInfo me)
    {
        return Functions.GetFriendlyName(me);
    }
    #endregion
    // Inspiration:
    //http://dnpextensions.codeplex.com/
    //http://www.extensionmethod.net/
}